package cn.taskservice.redis.lock.util;

import cn.taskservice.redis.lock.enums.LockType;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class RedissonUtil {

    private static final String LOCK_KEY_PREFIX = "lock:";

    @Autowired
    private RedissonClient redissonClient;

    /**
     * 获取锁（根据锁类型）
     */
    public RLock getLock(LockType type, String rawKey) {
        String lockKey = LOCK_KEY_PREFIX + rawKey;
        switch (type) {
            case FAIR_LOCK:
                return redissonClient.getFairLock(lockKey);
            case READ_LOCK:
                return redissonClient.getReadWriteLock(lockKey).readLock();
            case WRITE_LOCK:
                return redissonClient.getReadWriteLock(lockKey).writeLock();
            case REENTRANT_LOCK:
            default:
                return redissonClient.getLock(lockKey);
        }
    }

    /**
     * 阻塞加锁
     */
    public RLock lock(String key, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(redisKey(key));
        lock.lock(leaseTime, unit);
        return lock;
    }

    /**
     * 非阻塞尝试加锁（返回加锁是否成功）
     */
    public boolean tryLock(String key, long waitTime, long leaseTime, TimeUnit unit) {
        RLock lock = redissonClient.getLock(redisKey(key));
        try {
            return lock.tryLock(waitTime, leaseTime, unit);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("线程被中断，尝试加锁失败，key={}", key, e);
        } catch (Exception e) {
            log.error("尝试加锁失败，key={}", key, e);
        }
        return false;
    }

    /**
     * 非阻塞尝试加锁（返回加锁是否成功 + lock 实例）
     */
    public RLock tryLockWithInstance(LockType lockType, String key, long waitTime, long leaseTime, TimeUnit unit) {
        RLock lock = getLock(lockType,redisKey(key));
        try {
            boolean success = lock.tryLock(waitTime, leaseTime, unit);
            return success ? lock : null;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("线程被中断，尝试加锁失败，key={}", key, e);
        } catch (Exception e) {
            log.error("尝试加锁失败，key={}", key, e);
        }
        return null;
    }

    /**
     * 解锁（根据 key）
     */
    public void unlock(String key) {
        RLock lock = redissonClient.getLock(redisKey(key));
        unlock(lock);
    }

    /**
     * 解锁（根据 RLock 实例）
     */
    public void unlock(RLock lock) {
        if (lock == null) return;
        try {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            } else {
                log.warn("当前线程未持有锁，无法释放");
            }
        } catch (Exception e) {
            log.error("释放锁失败", e);
        }
    }
    /**
     * 启用 Watchdog 的阻塞锁（不设置 leaseTime）
     */
    public RLock lockWithWatchdog(String key) {
        RLock lock = redissonClient.getLock(redisKey(key));
        lock.lock(); // 自动续期
        return lock;
    }

    /**
     * 启用 Watchdog 的 tryLock（leaseTime 不指定）
     */
    public RLock tryLockWithWatchdog(LockType lockType, String key, long waitTime, TimeUnit unit) {
        RLock lock = getLock(lockType, redisKey(key));
        try {
            boolean success = lock.tryLock(waitTime, unit);
            return success ? lock : null;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("线程被中断，尝试加锁失败，key={}", key, e);
        } catch (Exception e) {
            log.error("尝试加锁失败，key={}", key, e);
        }
        return null;
    }

    private static String redisKey(String key){
        return LOCK_KEY_PREFIX+key;
    }
}
